import * as fs from "fs";
import * as path from "path";
import * as morph from "ts-morph";

const TagResponseType = "ResponseTypeAttribute";
const TagInterface = "Interface";

const ResponseTypeDecoratorType = "ResponseTypeAttribute";
const MessageDecoratorType = "MessageAttribute";

export function BuildMessage(filePath: string, outPath: string) {
    let nodeTypeList: string[] = [];
    let nodeParentList: string[] = [];
    let nodeCodeList: number[] = [];

    const basePath = path.dirname(filePath);
    const fileName = path.basename(filePath);

    const tempPath = path.join(basePath, "../Temp");

    const project = new morph.Project({ compilerOptions: { target: morph.ts.ScriptTarget.ES2016 } });
    project.addSourceFileAtPathIfExists(filePath);
    const morphSourceFile = project.getSourceFileOrThrow(filePath);

    const tempProject = new morph.Project({ compilerOptions: { target: morph.ts.ScriptTarget.ES2016 } });
    const tempMorphSourceFile = tempProject.createSourceFile("interface.ts", "");

    const newProject = new morph.Project({ compilerOptions: { target: morph.ts.ScriptTarget.ES2016 } });
    const newMorphSourceFile = newProject.createSourceFile("new.ts", "");

    const nameList = fileName.split("_");
    const fileClassName = nameList[0];
    const beginOpcode = parseInt(nameList[2]);

    newMorphSourceFile.addImportDeclaration({
        moduleSpecifier: "../../../Share/Module/Message/IMessage",
        namedImports: ["IMessage", "IRequest", "IResponse"],
    });

    newMorphSourceFile.addImportDeclaration({
        moduleSpecifier: "../../../Share/Module/Actor/IActorMessage",
        namedImports: ["IActorMessage", "IActorRequest", "IActorResponse"],
    });

    newMorphSourceFile.addImportDeclaration({
        moduleSpecifier: "../../../Share/Module/ActorLocation/IActorLocationMessage",
        namedImports: ["IActorLocationMessage", "IActorLocationRequest", "IActorLocationResponse"],
    });

    newMorphSourceFile.addImportDeclaration({
        moduleSpecifier: "../../../../../Decorator/Decorator",
        namedImports: ["MessageAttribute", "ResponseTypeAttribute"],
    });

    const protocolClassDecl = newMorphSourceFile.addClass({
        name: fileClassName
    });
    protocolClassDecl.setIsExported(true);

    let i = 0;
    const defineClassDecls = morphSourceFile.getClasses();
    defineClassDecls.forEach(defineClassDecl => {
        const currentOpcode = beginOpcode + ++i;

        const className = defineClassDecl.getName();

        // TSBuffer需要使用interface定义协议格式，但是interface没办法保持唯一，所有改为使用class定义，然后生成中间状态的interface
        const interfaceDecl = tempMorphSourceFile.addInterface({
            name: className
        });
        interfaceDecl.setIsExported(true);

        const classDecl = newMorphSourceFile.addClass({
            name: className
        });

        protocolClassDecl.addProperty({
            scope: morph.Scope.Public,
            isStatic: true,
            isReadonly: true,
            name: className,
            type: "number",
            initializer: currentOpcode.toString(),
        });

        let parentClass = "";
        let resposeType = "";
        const jsDocs = defineClassDecl.getJsDocs();
        if (jsDocs.length != 0) {
            const jsDoc = jsDocs[0];
            const tags = jsDoc.getTags();
            tags.forEach(tag => {
                const tagName = tag.getTagName();
                const tagType = tag.getCommentText();
                if (tagName == TagResponseType) {
                    resposeType = tagType;
                } else if (tagName == TagInterface) {
                    parentClass = tagType;
                }
            });
        }

        classDecl.setIsExported(true);
        if (parentClass != "") {
            classDecl.setExtends(parentClass);
        }

        nodeTypeList.push(className);
        nodeParentList.push(parentClass);
        nodeCodeList.push(currentOpcode);

        if (resposeType != "") {
            const responseTypeDecoratorNode = classDecl.addDecorator({
                name: ResponseTypeDecoratorType,
            });
            responseTypeDecoratorNode.addArguments([resposeType]);
        }

        const messageDecoratorNode = classDecl.addDecorator({
            name: MessageDecoratorType,
        });
        messageDecoratorNode.addArguments([`${fileClassName}.${className}`]);

        defineClassDecl.getProperties().forEach(property => {
            const propertyName = property.getName();
            const propertyTypeNode = property.getTypeNode()!;
            const propertyType = propertyTypeNode.getText();

            // 在新的类中添加属性，类型与接口一致
            interfaceDecl.addProperty({
                name: propertyName,
                type: propertyType,
                hasQuestionToken: property.hasQuestionToken(),
            });

            // 在新的类中添加属性，类型与接口一致
            classDecl.addProperty({
                name: propertyName,
                type: propertyType,
                hasQuestionToken: property.hasQuestionToken(),
            });
        });
    });

    tempMorphSourceFile.formatText();

    const tempFileContent = tempMorphSourceFile.getFullText();
    const tempFilePath = path.join(tempPath, fileName);

    console.log(`临时Proto已生成到: ${tempFilePath}`);

    fs.writeFileSync(tempFilePath, tempFileContent);

    newMorphSourceFile.formatText();

    const newFileContent = newMorphSourceFile.getFullText();
    const outFilePath = path.join(outPath, fileName);

    console.log(`Message已生成到: ${outFilePath}`);

    fs.writeFileSync(outFilePath, newFileContent);

    // console.log(newFileContent);
    // console.log(outFilePath);
    // console.log(nodeTypeList);
    // console.log(nodeParentList);
    // console.log(nodeCodeList);

    return { tempFilePath, nodeTypeList, nodeParentList, nodeCodeList };
}